﻿using System;
using System.Xml;
using System.IO.MemoryMappedFiles;
using System.Collections;
using LCL.Caching.Provider;
namespace LCL.Caching
{
    /// <summary>
    /// 对缓存进行全局控制管理]
    /// CacheManager
    /// </summary>
    public sealed class LCache
    {
        private static object lockHelper = new object();
        private ICacheProvider cacheProvider;
        private static volatile LCache instance = null;
        private static XmlDocument rootXml = new XmlDocument();
        private static XmlElement objectXmlMap;
        private LCache()
        {
            cacheProvider = new MemoryCacheProvider();
            if (rootXml.HasChildNodes)
                rootXml.RemoveAll();
            objectXmlMap = rootXml.CreateElement("Cache");
            //建立内部XML文档.
            rootXml.AppendChild(objectXmlMap);
        }
        public static LCache Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (lockHelper)
                    {
                        if (instance == null)
                        {
                            instance = new LCache();
                        }
                    }
                }
                return instance;
            }
        }
        public void LoadCacheProvider(ICacheProvider impl)
        {
            lock (lockHelper)
            {
                cacheProvider = impl;
            }
        }
        public void LoadDefaultCacheProvider()
        {
            lock (lockHelper)
            {
                cacheProvider = new MemoryCacheProvider();
            }
        }
        #region ICacheProvider Members
        private int _timeOut = 3600;
        public int TimeOut
        {
            set { _timeOut = value > 0 ? value : 3600; }
            get { return _timeOut > 0 ? _timeOut : 3600; }
        }
        public void Set(string key, object data, int cacheTime)
        {
            lock (lockHelper)
            {
                string cacheKey = AddXmlNode(key);
                //向缓存加入新的对象
                cacheProvider.Set(cacheKey, data, cacheTime);
            }
        }
        public void Set(string key, object data)
        {
            lock (lockHelper)
            {
                string cacheKey = AddXmlNode(key);
                cacheProvider.Set(cacheKey, data, TimeOut);
            }
        }
        public T Get<T>(string key)
        {
            lock (lockHelper)
            {
                XmlNode node = objectXmlMap.SelectSingleNode(PrepareXpath(key));
                if (node != null)
                {
                    string cacheKey = node.Attributes["objectId"].Value;
                    return cacheProvider.Get<T>(cacheKey);
                }
                return default(T);
            }
        }
        public T Get<T>(string key, Func<T> acquire)
        {
            return Get(key, TimeOut, acquire);
        }
        public T Get<T>(string key, int cacheTime, Func<T> acquire)
        {
            lock (lockHelper)
            {
                if (Exists(key))
                {
                    return Get<T>(key);
                }
                else
                {
                    var result = acquire();
                    if (cacheTime > 0)
                        Set(key, result, cacheTime);
                    return result;
                }
            }
        }
        public void Remove(string key)
        {
            lock (lockHelper)
            {
                try
                {
                    #region MyRegion
                    XmlNode result = objectXmlMap.SelectSingleNode(PrepareXpath(key));
                    //检查路径是否指向一个组或一个被缓存的实例元素
                    if (result.HasChildNodes)
                    {
                        //删除所有对象和子结点的信息
                        XmlNodeList objects = result.SelectNodes("*[@objectId]");
                        string objectId = "";
                        foreach (XmlNode node in objects)
                        {
                            objectId = node.Attributes["objectId"].Value;
                            node.ParentNode.RemoveChild(node);
                            //删除对象
                            cacheProvider.Remove(objectId);
                        }
                    }
                    else
                    {
                        //删除元素结点和相关的对象
                        string objectId = result.Attributes["objectId"].Value;
                        result.ParentNode.RemoveChild(result);
                        cacheProvider.Remove(objectId);
                    }
                    #endregion
                }
                catch
                { }
            }
        }
        public bool Exists(string key)
        {
            lock (lockHelper)
            {
                XmlNode node = objectXmlMap.SelectSingleNode(PrepareXpath(key));
                if (node != null)
                {
                    string cacheKey = node.Attributes["objectId"].Value;
                    return cacheProvider.Exists(cacheKey);
                }
                return false;
            }
        }
        public void Clear()
        {
            lock (lockHelper)
            {
                cacheProvider.Clear();
            }
        }
        #endregion
        #region objectXmlMap
        private string AddXmlNode(string xpath)
        {
            //整理XPATH表达式信息
            string newXpath = PrepareXpath(xpath);
            int separator = newXpath.LastIndexOf("/");
            //找到相关的组名
            string group = newXpath.Substring(0, separator);
            //找到相关的对象
            string element = newXpath.Substring(separator + 1);
            XmlNode groupNode = objectXmlMap.SelectSingleNode(group);
            //建立对象的唯一键值, 用以映射XML和缓存对象的键
            string objectId = "";
            XmlNode node = objectXmlMap.SelectSingleNode(PrepareXpath(xpath));
            if (node != null)
            {
                objectId = node.Attributes["objectId"].Value;
            }
            if (objectId == "")
            {
                groupNode = CreateNode(group);
                objectId = Guid.NewGuid().ToString();
                //建立新元素和一个属性 for this perticular object
                XmlElement objectElement = objectXmlMap.OwnerDocument.CreateElement(element);
                XmlAttribute objectAttribute = objectXmlMap.OwnerDocument.CreateAttribute("objectId");
                objectAttribute.Value = objectId;
                objectElement.Attributes.Append(objectAttribute);
                //为XML文档建立新元素
                groupNode.AppendChild(objectElement);
            }
            else
            {
                //建立新元素和一个属性 for this perticular object
                XmlElement objectElement = objectXmlMap.OwnerDocument.CreateElement(element);
                XmlAttribute objectAttribute = objectXmlMap.OwnerDocument.CreateAttribute("objectId");
                objectAttribute.Value = objectId;
                objectElement.Attributes.Append(objectAttribute);
                //为XML文档建立新元素
                groupNode.ReplaceChild(objectElement, node);
            }
            return objectId;
        }
        private XmlNode CreateNode(string xpath)
        {
            lock (lockHelper)
            {
                string[] xpathArray = xpath.Split('/');
                string root = "";
                XmlNode parentNode = objectXmlMap;
                //建立相关节点
                for (int i = 1; i < xpathArray.Length; i++)
                {
                    XmlNode node = objectXmlMap.SelectSingleNode(root + "/" + xpathArray[i]);
                    // 如果当前路径不存在则建立,否则设置当前路径到它的子路径上
                    if (node == null)
                    {
                        XmlElement newElement = objectXmlMap.OwnerDocument.CreateElement(xpathArray[i]);
                        parentNode.AppendChild(newElement);
                    }
                    //设置低一级的路径
                    root = root + "/" + xpathArray[i];
                    parentNode = objectXmlMap.SelectSingleNode(root);
                }
                return parentNode;
            }
        }
        private string PrepareXpath(string xpath)
        {
            lock (lockHelper)
            {
                string[] xpathArray = xpath.Split('/');
                xpath = "/Cache";
                foreach (string s in xpathArray)
                {
                    if (s != "")
                    {
                        xpath = xpath + "/" + s;
                    }
                }
                return xpath;
            }
        }
        #endregion
    }
}
